// Author: Shafee Haque
// Date:   11/29/2009
// Status: working

package edu.columbia.plt.visage;

import java.awt.*;
import java.awt.geom.*;
import java.util.List;
import java.util.*;

import javax.swing.*;

public class Plot extends Canvas {
	
	//variables
	private Map<String, DataSet> map = new Hashtable<String, DataSet>();
	private int time = 0, height, width;
	private double xmax = 10, ymin = 0, ymax = 10, //plot variables
			datamin = Double.MAX_VALUE, datamax = Double.MIN_VALUE; //data variables
	
	//constants
	private static final long serialVersionUID = -1390807420377819011L; //??? 
	private static final Color[] colors = {Color.blue, Color.red, 
			Color.green, Color.magenta, Color.cyan, Color.darkGray};
	private static final int xoff = 30, yoff = 15;
	
	
	public Plot(){
		
		this(400, 300);
	}
	
	public Plot(int w, int h){
		JFrame frame = new JFrame();
    	frame.setLayout(new FlowLayout());    
		frame.add(this);
    	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	frame.setSize(w, h);
    	
		height = h - yoff * 2;		
		width  = w - xoff * 2;
    	//super("Visage Plot");
		setSize(width, height);
    	//setBackground(Color.WHITE);
    	//setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBackground(Color.WHITE);
    	setVisible(true);
    	validate();
    	
    	frame.pack();
    	frame.setVisible(true);
		
	}
	
	private class DataSet{
		public List<Point2D.Double> dataPts = new ArrayList<Point2D.Double>();
		public Color color;
		public int lastx = -1;
		public DataSet(Color c){
			color = c;
		}
	}

	//icon interface functions
	public int getIconHeight() {
		return height;
	}

	public int getIconWidth() {
		return width;
	}

	//paints entire plot
	public void update(Graphics g) {
		super.update(g);
		paint(g);
	}
	
	public void paint(Graphics g) {
		while (time > xmax)
			xmax *= Math.round(10*(Math.log10(xmax)%1)) == 3 ? 2.5 : 2;
		while (datamax > ymax)
			ymax *= Math.round(10*(Math.log10(ymax)%1)) == 3 ? 2.5 : 2;
		while (datamin < ymin)
			ymin = ymin >= 0 ? -10 : ymin * (Math.round(10*(Math.log10(ymin)%1)) == 3 ? 2.5 : 2);
		Graphics2D g2 = (Graphics2D) g;
		int a = xoff, b = height + yoff;
		g2.drawString("Key:", a, b);
		for(String key: map.keySet()){
			g2.setColor(map.get(key).color);
			g2.drawString(key, a += 100, b);
			List<Point2D.Double> pts = map.get(key).dataPts;
			if(pts == null || pts.size() < 2)
				continue;
			Point2D.Double left = plotPt(pts.get(0)), right;
			for(int i = 1; i < pts.size(); i++){
				right = plotPt(pts.get(i));
				g2.draw(new Line2D.Double(left, right));
				left = right;
			}
		}
		g2.setColor(Color.black);
		g2.drawRect(xoff - 5, b - 12, a + 50, 20);
		//axes
		g2.draw(new Line2D.Double(plotPt(new Point2D.Double(0,0)), plotPt(new Point2D.Double(xmax,0))));
		g2.draw(new Line2D.Double(plotPt(new Point2D.Double(0,ymin)), plotPt(new Point2D.Double(0,ymax))));
		for(int n=5, x=0, y=0; x < xmax; x +=(int)(xmax/n), y +=(int)(Math.max(ymax,-ymin)/n)){
			Point2D.Double p = plotPt(new Point2D.Double(x,0));
			g2.drawLine((int)p.x, (int)p.y - 3, (int)p.x, (int)p.y + 3);
			g2.drawString("" + x, (int)p.x, (int)p.y + yoff);
			p = plotPt(new Point2D.Double(0,y));
			g2.drawLine((int)p.x - 3, (int)p.y, (int)p.x + 3, (int)p.y);
			g2.drawString("" + y, (int)p.x - xoff, (int)p.y);
			p = plotPt(new Point2D.Double(0,-y));
			g2.drawLine((int)p.x - 3, (int)p.y, (int)p.x + 3, (int)p.y);
			if(y > 0) g2.drawString("-" + y, (int)p.x - xoff, (int)p.y);
		}
		
	}
	//takes data point and returns corresponding plot point
	private Point2D.Double plotPt(Point2D.Double dataPt){
		return new Point2D.Double(dataPt.x / xmax * width + xoff, 
				(ymax - dataPt.y) / (ymax - ymin) * height - yoff);
	}
	
	//the main function: add point data to plot with key label
	public void plot(String label, double data){
		if(! map.containsKey(label))
			map.put(label, new DataSet(colors[map.keySet().size() % colors.length]));
		DataSet d = map.get(label);
		if (d.lastx == time)
			time++;
		d.lastx = time;
		d.dataPts.add(new Point2D.Double(time, data));
		datamin = Math.min(data, datamin);
		datamax = Math.max(data, datamax);
		if(d.dataPts.size() > 1){
			repaint();
			//System.out.println(label + data);
			/*
			if(time > xmax || datamax > ymax || datamin < ymin)
				update(getGraphics());
			else {
				Graphics2D g2 = (Graphics2D) getGraphics();
				if (g2 == null){
					System.out.println("g2 null called with " + label + data);
					return;
				}
				g2.setColor(d.color);
				g2.draw(new Line2D.Double(d.dataPts.get(d.dataPts.size() - 2), 
						d.dataPts.get(d.dataPts.size() - 1)));
			}
			*/
		}
	}


	//test
	public static void main(String[] args) throws InterruptedException{
		JFrame frame = new JFrame();
		Plot p = new Plot();
    	frame.setLayout(new FlowLayout());    
		frame.add(p);
    	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	frame.pack();
    	frame.setVisible(true);
    	
    	for(int i = 0; i < 100; i++){
    		p.plot("x", i*i/3);
    		p.plot("label y", i*(90 - i));
    		if(i % 30 == 0) p.plot("z", i*i/3);
    		p.plot("r", i * (20 + 5*Math.random()));
    		Thread.sleep(100);
    	}
	}
	
}
